﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Media;


namespace Scopa.GameSound
{

    public interface ISoundManager<T>
    {
        void PlaySound(T sound);
        void PlaySound(T sound, float volume, float pitch, float pan);
        void PlayNextSong();
        string GetSongName();

        void StopAllSounds();
        void StopMusic();

        // The sound effects volume
        float SfxVolume { get; set; }
        bool SfxMuted { get; set; }

        // The music volume
        float MusicVolume { get; set; }
        bool MusicMuted { get; set; }
    }

    public abstract class SoundManager<T> : Microsoft.Xna.Framework.GameComponent, Scopa.GameSound.ISoundManager<T>
    {
        // Sound array
        protected SoundEffect[] soundLibrary;
        protected const int MaxSounds = 32;

        // Played sounds list
        protected SoundEffectInstance[] playingSounds;

        // The sound effects volume
        public float SfxVolume { get; set; }
        public bool SfxMuted { get; set; }

        // Music library
        protected MediaLibrary mediaLibrary;

        // The music volume
        public float MusicVolume { get { return MediaPlayer.Volume; } set { MediaPlayer.Volume = value; } }
        public bool MusicMuted { get; set; }

        public SoundManager(Game game)
            : base(game)
        {
            game.Services.AddService(typeof(ISoundManager<T>), this);
        }

        protected override void Dispose(bool disposing)
        {
            // Dispose all the sound effects that are playing
            for (int i = 0; i < playingSounds.Length; ++i)
            {
                if (playingSounds[i] != null)
                {
                    playingSounds[i].Dispose();
                    playingSounds[i] = null;
                }
            }

            Game.Services.RemoveService(typeof(ISoundManager<T>));
            base.Dispose(disposing);
        }

        public override void Initialize()
        {
            //soundLibrary = new SoundEffect[MaxSounds];
            mediaLibrary = new MediaLibrary();
            //MusicVolume = 0;
            playingSounds = new SoundEffectInstance[MaxSounds];

            // Load the audio configuration for the current user
            var context = Game.GetScopaContext();
            SfxMuted = context.currentConfig.sfxMuted;
            SfxVolume = context.currentConfig.sfxVolume;
            MusicMuted = context.currentConfig.musicMuted;
            MusicVolume = context.currentConfig.musicVolume;

            base.Initialize();
        }

        public override void Update(GameTime gameTime)
        {
            // Keeps the sound instances collection updated
            for (int i = 0; i < playingSounds.Length; ++i)
            {
                if (playingSounds[i] != null && playingSounds[i].State == SoundState.Paused)
                {
                    playingSounds[i].Dispose();
                    playingSounds[i] = null;
                }
            }

            // Check if it's time to change song
            //if (MediaPlayer.State != MediaState.Playing && !MusicMuted)
            //  PlayNextSong();

            base.Update(gameTime);
        }


        public abstract void PlaySound(T sound);

        public abstract void PlaySound(T sound, float volume, float pitch, float pan);

        /// <summary>
        /// Plays another song chosen randomly in the library
        /// </summary>
        public void PlayNextSong()
        {
            if (MusicMuted) return;
            //if (MediaPlayer.State == MediaState.Paused)
            //  MediaPlayer.Resume();

            if (MediaPlayer.Queue.ActiveSong != null)
                MediaPlayer.Queue.ActiveSong.Dispose();

            if (mediaLibrary.Albums.Count > 0)
            {
                int albumNumber = (new Random()).Next(0, mediaLibrary.Albums.Count);
                int songNumber = (new Random()).Next(0, mediaLibrary.Albums[albumNumber].Songs.Count);
                MediaPlayer.Play(mediaLibrary.Albums[albumNumber].Songs[songNumber]);
            }
        }

        /// <summary>
        /// Stops all playing sounds
        /// </summary>
        public void StopAllSounds()
        {
            for (int i = 0; i < playingSounds.Length; ++i)
            {
                if (playingSounds[i] != null)
                {
                    playingSounds[i].Stop();
                    playingSounds[i].Dispose();
                    playingSounds[i] = null;
                }
            }
        }

        /// <summary>
        /// Stops the playing music
        /// </summary>
        public void StopMusic()
        {
            if (MediaPlayer.State == MediaState.Playing)
                MediaPlayer.Stop();
            //MediaPlayer.Pause();
        }

        /// <summary>
        /// Adds a new played sound to the playing sounds list
        /// </summary>
        /// <param name="sndInst">The sound instance to add to the list</param>
        protected void AddToPlayingQueue(SoundEffectInstance sndInst)
        {
            int nextAvailableIdx = GetAvailableSoundIndex();
            if (nextAvailableIdx != -1)
                playingSounds[nextAvailableIdx] = sndInst;
        }

        /// <summary>
        /// Gets the next available index in the sound instances collection
        /// </summary>
        /// <returns>The first available position in the sound instances collection</returns>
        private int GetAvailableSoundIndex()
        {
            for (int i = 0; i < MaxSounds; ++i)
            {
                if (playingSounds[i] == null)
                    return i;
            }
            return -1;
        }

        /// <summary>
        /// Returns the artist and title of the song currently playing
        /// </summary>
        /// <returns>A string containing artist and title of the song</returns>
        public string GetSongName()
        {
            if (MediaPlayer.State == MediaState.Playing)
            {
                Song activeSong = MediaPlayer.Queue.ActiveSong;
                return activeSong.Artist + " - " + activeSong.Name;
            }
            return "";
        }

    }
}
